package cn.yzgy.yzgyoa.wxpay;

import cn.yzgy.yzgyoa.wxpay.sdk.WXPayReport;
import cn.yzgy.yzgyoa.wxpay.sdk.WXPayUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.java.Log;
import org.apache.commons.codec.Charsets;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

/**
 * 微信支付接口
 * 提供给前端调用
 * 大致有 获取到code,根据code获取到openId,根据OpenId调用统一下单接口，获取到预支付ID，然后将相关参数返回给前端，
 * 前端通过这些参数，调用JSAPI支付接口，然后根据返回状态做出相应的操作
 * 后端在前端调用JSAPI成功支付后，微信会回调接口（注意所有回调系统端口应该是80），在回调接口里面，根据状态，处理相应的业务逻辑
 */
@Log
@Controller
@RequestMapping("/wx")
public class WxPayController {

    @RequestMapping("/payH5")
    @ResponseBody
    public Object payH5(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> json)throws Exception {
        try {
            String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
            //记得自己把订单id保存下来 orderNo
            String code = json.get("code").toString();
            /** 这里是根据code想要获取openId */
            String res = get("https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + WxConfig.appId + "&secret="
                    + WxConfig.secret + "&code=" + code + "&grant_type=authorization_code");
            JSONObject json1 = JSONObject.parseObject(res);
            String openId = json1.getString("openid"); //获取的用户唯一标识
            System.out.println(openId+"------------------------------");
            Map<String, String> paraMap = new HashMap<>();
            paraMap.put("appid", WxConfig.appId);//公众号appid
            paraMap.put("body", json.get("body").toString());//商品说明
            paraMap.put("mch_id", WxConfig.mchId);//商家id
            paraMap.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
            paraMap.put("openid", openId);//openid 用户在公众号内的唯一标识符
            paraMap.put("out_trade_no", orderNo);//商户订单号
            paraMap.put("spbill_create_ip", "120.79.1.1");//终端IP 用户的客户端IP
            paraMap.put("total_fee","123");//标价金额 单位（分）
            paraMap.put("trade_type", WxConfig.tradeType);//交易类型
            //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。公网域名必须为https，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用http
            paraMap.put("notify_url","http://yunkt.zmxzc.com//api/wx/callBack");//通知地址
            String apiKey = WxConfig.key;
            System.out.println(WXPayUtil.mapToXml(paraMap));
            String sign = WXPayUtil.generateSignature(paraMap, apiKey);
            paraMap.put("sign", sign);
            String xml = WXPayUtil.mapToXml(paraMap);
            System.out.println(xml);
            // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder
            String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

            String xmlStr = WXPayReport.httpRequest(unifiedorder_url, xml, 2000, 5000);
            // 以下内容是返回前端页面的json数据
            System.out.println(xmlStr);
            String prepay_id = "";// 预支付id
            if (xmlStr.indexOf("SUCCESS") != -1) {
                Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
                prepay_id = (String) map.get("prepay_id");
            }
            Map<String, String> payMap = new HashMap<String, String>();
            payMap.put("appId", WxConfig.appId);
            payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");
            payMap.put("nonceStr", WXPayUtil.generateNonceStr());
            payMap.put("signType", "MD5");
            payMap.put("package", "prepay_id=" + prepay_id);
            String paySign = WXPayUtil.generateSignature(payMap, WxConfig.key);
            payMap.put("paySign", paySign);
            payMap.put("orderNo", orderNo);
            payMap.put("success", "0");
            return payMap;

        } catch (Exception e) {
            e.printStackTrace();
            Map<String, String> payMap = new HashMap<String, String>();
            payMap.put("success", "1");
            return payMap;

        }
//        return new SuccessResponseData();
    }

    public String get(String url) throws Exception {
        // 创建HttpClient实例
        CloseableHttpClient client = HttpClientBuilder.create().build();
        // 根据URL创建HttpGet实例
        HttpGet get = new HttpGet(url);
        // 执行get请求，得到返回体
        HttpResponse response = client.execute(get);
        // 判断是否正常返回
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            // 解析数据
            String data = EntityUtils.toString(response.getEntity(), Charsets.UTF_8);
            System.out.println(data);
            return data;
        }
        return "";
    }


    /**
     * 主动调用微信后台，查询订单状态
     * 使用场景
     * ◆ 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知（查单实现可参考：支付回调和查单实现指引）；
     * ◆ 调用支付接口后，返回系统错误或未知交易状态情况；
     * ◆ 调用付款码支付API，返回USERPAYING的状态；
     * ◆ 调用关单或撤销接口API之前，需确认支付状态
     * @return
     */
    @RequestMapping("/queryOrder")
    @ResponseBody
    public Object queryPayResult() throws Exception {
        Map<String, String> paraMap = new HashMap<>();
        paraMap.put("appid", WxConfig.appId);//公众号ID
        paraMap.put("mch_id", WxConfig.mchId);//商户号
        paraMap.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
        /** 以下两个参数2选1，优先使用 transaction_id **/
        paraMap.put("out_trade_no", "123");//商户订单号
        paraMap.put("transaction_id", "123");//微信订单号，应该是预付款订单ID
//        paraMap.put("spbill_create_ip", "120.79.1.1");
        String apiKey = WxConfig.key;
        String sign = WXPayUtil.generateSignature(paraMap, apiKey);
        paraMap.put("sign", sign);
        String xml = WXPayUtil.mapToXml(paraMap);
        System.out.println(xml);
        // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder
        String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/orderquery";

        String xmlStr = WXPayReport.httpRequest(unifiedorder_url, xml, 2000, 5000);
        // 以下内容是返回前端页面的json数据
        System.out.println(xmlStr);

        if (xmlStr.indexOf("SUCCESS") != -1) {
            Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
            if(map.get("trade_state")!=null&&((String) map.get("trade_state")).equals("SUCCESS")){
                //修改本地订单状态
                //这里的订单就是前面的String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
            }
        }
        return "";
    }

    /**
     * 根据传入参数获取签名
     * @param paraMap
     * @return
     * @throws Exception
     */
    private String getSign(Map<String,String> paraMap) throws Exception {
        String apiKey = WxConfig.key;
        String sign = WXPayUtil.generateSignature(paraMap, apiKey);
        return sign;
    }

    /**
     * 微信支付回调函数
     * 1.获取通知数据
     * 2.验证签名
     * 3.验证业务结果
     * 4.验证订单号和金额
     * 5.修改订单状态，处理响应业务逻辑
     * @param request
     * @param response
     */
    @RequestMapping("callBack")
    public void callBack(HttpServletRequest request,HttpServletResponse response) throws Exception {
        //读取参数
        InputStream inputStream ;
        StringBuffer sb = new StringBuffer();
        inputStream = request.getInputStream();
        String s ;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null){
            sb.append(s);
        }
        in.close();
        inputStream.close();

        //解析xml成map
        Map<String, String> m = WXPayUtil.xmlToMap(sb.toString());

        //过滤空 设置 TreeMap
        SortedMap<Object,Object> packageParams = new TreeMap<>();
        Iterator it = m.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = m.get(parameter);

            String v = "";
            if(null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        // 账号信息
        String key = WxConfig.key; // key

        log.info("packageParams:{}"+packageParams);
        //判断签名是否正确
        if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
            //------------------------------
            //处理业务开始
            //------------------------------
            String resXml = "";
            if("SUCCESS".equals((String)packageParams.get("result_code"))){
                // 这里是支付成功
                //执行自己的业务逻辑
                String mch_id = (String)packageParams.get("mch_id");
                String openid = (String)packageParams.get("openid");
                String is_subscribe = (String)packageParams.get("is_subscribe");
                String out_trade_no = (String)packageParams.get("out_trade_no");

                String total_fee = (String)packageParams.get("total_fee");

                log.info("mch_id:"+mch_id);
                log.info("openid:"+openid);
                log.info("is_subscribe:"+is_subscribe);
                log.info("out_trade_no:"+out_trade_no);
                log.info("total_fee:"+total_fee);

                //执行自己的业务逻辑

                log.info("支付成功");
                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

            } else {
                log.info("支付失败,错误信息：" + packageParams.get("err_code"));
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
            //------------------------------
            //处理业务完毕
            //------------------------------
            BufferedOutputStream out = new BufferedOutputStream(
                    response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } else{
            log.info("通知签名验证失败");
        }
    }
}
